﻿// MIT 许可证
//
// 版权 (c) 2020-present 百小僧, 百签科技（广东）有限公司 和所有贡献者
//
// 特此免费授予任何获得本软件副本和相关文档文件（下称“软件”）的人不受限制地处置该软件的权利，
// 包括不受限制地使用、复制、修改、合并、发布、分发、转授许可和/或出售该软件副本，
// 以及再授权被配发了本软件的人如上的权利，须在下列条件下：
//
// 上述版权声明和本许可声明应包含在该软件的所有副本或实质成分中。
//
// 本软件是“如此”提供的，没有任何形式的明示或暗示的保证，包括但不限于对适销性、特定用途的适用性和不侵权的保证。
// 在任何情况下，作者或版权持有人都不对任何索赔、损害或其他责任负责，无论这些追责来自合同、侵权或其它行为中，
// 还是产生于、源于或有关于本软件以及本软件的使用或其它处置。

using Microsoft.AspNetCore.Http;
using Sundial.Dashboard.backend;
using System.Text;
using System.Text.Encodings.Web;
using System.Text.Json;
using System.Text.Json.Serialization;
using TimeCrontab;

namespace Sundial;

/// <summary>
/// Schedule 模块 UI 中间件
/// </summary>
public sealed class ScheduleUIMiddleware
{
    /// <summary>
    /// 固定请求路径
    /// </summary>
    internal const string REQUEST_PATH = "/schedule";

    private const string API_REQUEST_PATH = $"{REQUEST_PATH}/api";

    /// <summary>
    /// 请求委托
    /// </summary>
    private readonly RequestDelegate _next;

    /// <summary>
    /// 作业计划工厂
    /// </summary>
    private readonly ISchedulerFactory _schedulerFactory;

    /// <summary>
    /// 构造函数
    /// </summary>
    /// <param name="next">请求委托</param>
    /// <param name="schedulerFactory">作业计划工厂</param>
    public ScheduleUIMiddleware(RequestDelegate next
        , ISchedulerFactory schedulerFactory)
    {
        _next = next;
        _schedulerFactory = schedulerFactory;
    }

    /// <summary>
    /// 中间件执行方法
    /// </summary>
    /// <param name="context"><see cref="HttpContext"/></param>
    /// <returns><see cref="Task"/></returns>
    public async Task InvokeAsync(HttpContext context)
    {
        // 如果不是以 API_REQUEST_PATH 开头，则跳过
        if (!context.Request.Path.StartsWithSegments(API_REQUEST_PATH))
        {
            await _next(context);
            return;
        }

        // 只处理 GET/POST 请求
        if (context.Request.Method.ToUpper() != "GET" && context.Request.Method.ToUpper() != "POST")
        {
            await _next(context);
            return;
        }

        // 获取匹配的路由标识
        var action = context.Request.Path.Value?[API_REQUEST_PATH.Length..]?.ToLower();

        // 允许跨域，设置返回 json
        context.Response.ContentType = "application/json; charset=utf-8";
        context.Response.Headers["Access-Control-Allow-Origin"] = "*";
        context.Response.Headers["Access-Control-Allow-Headers"] = "*";

        // 路由匹配
        switch (action)
        {
            // 获取所有作业
            case "/get-jobs":
                await GetJobs(context);
                break;
            // 操作作业
            case "/operate-job":
                await OpJobsManager(context);
                break;
            // 操作触发器
            case "/operate-trigger":
                await OpTriggerManager(context);
                break;
            case "/job-save":
                await JobSave(context);
                break;
            case "/trigger-save":
                await TriggerSave(context);
                break;
        }
    }

    /// <summary>
    /// 所有作业列表
    /// </summary>
    /// <param name="context"></param>
    /// <returns></returns>
    async Task GetJobs(HttpContext context)
    {
        try
        {
            var jobs = _schedulerFactory.GetJobsOfModels();
            Result result = new Result(StatusCodes.Status200OK, "", jobs);
            await context.Response.WriteAsync(SerializeToJson(result));
        }
        catch(Exception ex)
        {
            Result result = new Result(StatusCodes.Status500InternalServerError, ex.Message, null);
            await context.Response.WriteAsync(SerializeToJson(result));
        }
    }
    async Task OpJobsManager(HttpContext context)
    {
        // 获取作业 Id
        var jobId = context.Request.Query["jobid"];
        // 获取操作方法
        var operate = context.Request.Query["action"];

        // 获取作业计划
        var scheduleResult = _schedulerFactory.TryGetJob(jobId, out var scheduler);

        // 处理找不到作业情况
        if (scheduleResult != ScheduleResult.Succeed)
        {
            // 标识状态码为 500
            context.Response.StatusCode = StatusCodes.Status500InternalServerError; 
            // 输出 JSON
            Result result = new Result(StatusCodes.Status500InternalServerError, scheduleResult.ToString(), null);
            await context.Response.WriteAsync(SerializeToJson(result));

            return;
        }

        switch (operate)
        {
            // 启动作业
            case "start":
                scheduler?.Start();
                break;
            // 暂停作业
            case "pause":
                scheduler?.Pause();
                break;
            // 移除作业
            case "remove":
                _schedulerFactory.RemoveJob(jobId);
                break;
        }

        // 输出 JSON
        Result result2 = new Result(StatusCodes.Status200OK, ScheduleResult.Succeed.ToString(), null);
        await context.Response.WriteAsync(SerializeToJson(result2));
    }
    async Task OpTriggerManager(HttpContext context)
    {
        // 获取作业 Id
        var jobId1 = context.Request.Query["jobid"];
        var triggerId = context.Request.Query["triggerid"];
        // 获取操作方法
        var operate1 = context.Request.Query["action"];

        // 获取作业计划
        var scheduleResult1 = _schedulerFactory.TryGetJob(jobId1, out var scheduler1);

        // 处理找不到作业情况
        if (scheduleResult1 != ScheduleResult.Succeed)
        {
            // 标识状态码为 500
            context.Response.StatusCode = StatusCodes.Status500InternalServerError; 
            // 输出 JSON
            Result result = new Result(StatusCodes.Status500InternalServerError, scheduleResult1.ToString(), null);
            await context.Response.WriteAsync(SerializeToJson(result));

            return;
        }

        switch (operate1)
        {
            // 启动作业触发器
            case "start":
                scheduler1?.StartTrigger(triggerId);
                break;
            // 暂停作业触发器
            case "pause":
                scheduler1?.PauseTrigger(triggerId);
                break;
            // 移除作业触发器
            case "remove":
                scheduler1.RemoveTrigger(triggerId);
                break;
            // 获取作业触发器最近运行时间
            case "timelines":
                try
                {
                    var trigger = scheduler1?.GetTrigger(triggerId);
                    var timelines = trigger?.GetTimelines() ?? Array.Empty<TriggerTimeline>();
                    Result result = new Result(StatusCodes.Status200OK, "", timelines);
                    await context.Response.WriteAsync(SerializeToJson(result));
                }
                catch (Exception ex)
                {
                    Result result = new Result(StatusCodes.Status500InternalServerError, ex.Message, null);
                    await context.Response.WriteAsync(SerializeToJson(result));
                }
                return;
        }

        // 输出 JSON
        Result result2 = new Result(StatusCodes.Status200OK, ScheduleResult.Succeed.ToString(), null);
        await context.Response.WriteAsync(SerializeToJson(result2));
    }

    async Task JobSave(HttpContext context)
    {
        HttpRequest request = context.Request;
        Stream straem = request.Body;
        Encoding encoding = Encoding.UTF8;
        string jobPars = string.Empty;
        using (StreamReader reader = new StreamReader(straem, encoding))
        {
            jobPars = reader.ReadToEndAsync().Result;
        }

        try
        {
            JobDetailInput _detail = (JobDetailInput)JsonSerializer.Deserialize(jobPars, typeof(JobDetailInput),new JsonSerializerOptions(JsonSerializerDefaults.Web));
            IScheduler jobinfo = _schedulerFactory.GetJob(_detail.jobId);
            if (jobinfo != null)
            { 
                jobinfo.Pause();
                _schedulerFactory.RemoveJob(_detail.jobId);
            }

            List<TriggerBuilder> triggers = new List<TriggerBuilder>();
            var jobBuilder = JobBuilder.From(jobPars);
            if (_detail.cronList != null && _detail.cronList.Count > 0)
            {
                foreach (var cron in _detail.cronList)
                {
                    triggers.Add(Triggers.Cron(cron.cron, CronStringFormat.WithSecondsAndYears));
                }
            }
            _schedulerFactory.AddJob(jobBuilder, triggers.ToArray());

            Result result = new Result(StatusCodes.Status200OK,"", _detail);
            await context.Response.WriteAsync(SerializeToJson(result));
        }
        catch (Exception ex)
        {
            Result result = new Result(StatusCodes.Status500InternalServerError, ex.Message, null);
            await context.Response.WriteAsync(SerializeToJson(result));
        }
    }

    async Task TriggerSave(HttpContext context)
    {
        await context.Response.WriteAsync(SerializeToJson(new Trigger { }));
    }
    /// <summary>
    /// 将对象输出为 JSON 字符串
    /// </summary>
    /// <param name="obj">对象</param>
    /// <returns><see cref="string"/></returns>
    private static string SerializeToJson(object obj)
    {
        // 初始化默认序列化选项
        var jsonSerializerOptions = new JsonSerializerOptions
        {
            PropertyNameCaseInsensitive = true,
            ReadCommentHandling = JsonCommentHandling.Skip,
            Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
            AllowTrailingCommas = true,
            PropertyNamingPolicy = JsonNamingPolicy.CamelCase
        };

        // 处理时间类型
        var libraryAssembly = typeof(Schedular).Assembly;
        var dateTimeJsonConverter = Activator.CreateInstance(libraryAssembly.GetType($"{libraryAssembly.GetName().Name}.DateTimeJsonConverter"));
        jsonSerializerOptions.Converters.Add(dateTimeJsonConverter as JsonConverter);

        return JsonSerializer.Serialize(obj, jsonSerializerOptions);
    }
}